package cn.schoolwow.quickdao.module.database.ddl.action.flow.index;

import cn.schoolwow.quickdao.domain.QuickDAOConfig;
import cn.schoolwow.quickdao.domain.database.ddl.IndexField;
import cn.schoolwow.quickdao.domain.database.parent.DatabaseType;
import cn.schoolwow.quickdao.module.database.parent.domain.ResultSetConsumer;
import cn.schoolwow.quickdao.module.database.parent.flow.executor.ExecuteQueryConnectionFlow;
import cn.schoolwow.quickdao.module.database.parent.flow.executor.ExecuteUpdateConnectionFlow;
import cn.schoolwow.quickflow.domain.FlowContext;
import cn.schoolwow.quickflow.flow.BusinessFlow;

import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class CreateIndexFlow implements BusinessFlow {
    @Override
    public void executeBusinessFlow(FlowContext flowContext) throws Exception {
        setIndexName(flowContext);
        checkPostgres(flowContext);
        setIndexColumnNameStatement(flowContext);
        setStatement(flowContext);
        executeStatement(flowContext);
    }

    @Override
    public String name() {
        return "创建索引";
    }

    private void setIndexName(FlowContext flowContext){
        IndexField indexField = (IndexField) flowContext.checkData("indexField");

        if(null==indexField.indexName||indexField.indexName.isEmpty()){
            StringBuilder indexNameBuilder = new StringBuilder(indexField.tableName+"_"+indexField.indexType.name().toLowerCase()+"_");
            for (String column : indexField.columns) {
                indexNameBuilder.append(column + ",");
            }
            indexNameBuilder.deleteCharAt(indexNameBuilder.length()-1);
            indexField.indexName = indexNameBuilder.toString();
        }
    }

    private void checkPostgres(FlowContext flowContext){
        IndexField indexField = (IndexField) flowContext.checkData("indexField");
        //postgre数据库的索引要求全局唯一
        DatabaseType databaseType = flowContext.checkInstanceData(DatabaseType.class);

        switch (databaseType){
            case Postgresql:{
                flowContext.startFlow(new ExecuteQueryConnectionFlow())
                        .putTemporaryData("name", "查询数据库索引是否已存在")
                        .putTemporaryData("sql", "select tablename,indexname,indexdef from pg_indexes where indexname = ?;")
                        .putTemporaryData("parameters", Arrays.asList(indexField.indexName))
                        .putReturnData("resultSetConsumer",new ResultSetConsumer() {
                            @Override
                            public void consumeResultSet(ResultSet resultSet) throws Exception {
                                List<String> existIndexTableNameList = new ArrayList<>();
                                while(resultSet.next()) {
                                    existIndexTableNameList.add(resultSet.getString("tablename"));
                                }
                                flowContext.putTemporaryData("existIndexTableNameList", existIndexTableNameList);
                            }
                        })
                        .execute();
            }break;
        }
        List<String> existIndexTableNameList = (List<String>) flowContext.getData("existIndexTableNameList");
        if(null!=existIndexTableNameList&&!existIndexTableNameList.isEmpty()){
            throw new IllegalArgumentException("Postgres数据库索引名必须全局唯一!索引名:"+indexField.indexName+",已存在该索引的表名:"+existIndexTableNameList);
        }
    }

    private void setIndexColumnNameStatement(FlowContext flowContext){
        QuickDAOConfig quickDAOConfig = flowContext.checkInstanceData(QuickDAOConfig.class);
        IndexField indexField = (IndexField) flowContext.checkData("indexField");

        StringBuilder indexColumnNameStatementBuilder = new StringBuilder();
        indexColumnNameStatementBuilder.append("(");
        for (String column : indexField.columns) {
            indexColumnNameStatementBuilder.append(quickDAOConfig.databaseContext.databaseProvider.escape(column) + ",");
        }
        indexColumnNameStatementBuilder.deleteCharAt(indexColumnNameStatementBuilder.length() - 1);
        indexColumnNameStatementBuilder.append(")");
        flowContext.putCurrentFlowData("indexColumnNameStatement", indexColumnNameStatementBuilder.toString());
    }

    private void setStatement(FlowContext flowContext) {
        DatabaseType databaseType = flowContext.checkInstanceData(DatabaseType.class);
        QuickDAOConfig quickDAOConfig = flowContext.checkInstanceData(QuickDAOConfig.class);
        IndexField indexField = (IndexField) flowContext.checkData("indexField");
        String indexColumnNameStatement = (String) flowContext.checkData("indexColumnNameStatement");

        StringBuilder sqlBuilder = new StringBuilder("create");
        switch (indexField.indexType) {
            case NORMAL: {
            }
            break;
            case UNIQUE: {
                sqlBuilder.append(" unique");
            }
            break;
            case FULLTEXT: {
                sqlBuilder.append(" fulltext");
            }
            break;
        }
        sqlBuilder.append(" index " + quickDAOConfig.databaseContext.databaseProvider.escape(indexField.indexName) + " on " + quickDAOConfig.databaseContext.databaseProvider.escape(indexField.tableName));
        switch (databaseType){
            case Postgresql:{
                if(null!=indexField.using&&!indexField.using.isEmpty()){
                    sqlBuilder.append(" using " + indexField.using);
                }
                sqlBuilder.append(indexColumnNameStatement);
            }break;
            case SQLite:{
                if(null!=indexField.using&&!indexField.using.isEmpty()){
                    flowContext.remark("SQLite不支持using语法");
                }
            }
            default:{
                sqlBuilder.append(indexColumnNameStatement);
            }break;
        }
        if (null != indexField.comment && !indexField.comment.isEmpty()) {
            sqlBuilder.append(" " + quickDAOConfig.databaseContext.databaseProvider.comment(indexField.comment));
        }
        sqlBuilder.append(";");
        flowContext.putTemporaryData("sql", sqlBuilder.toString());
    }

    private void executeStatement(FlowContext flowContext) {
        flowContext.startFlow(new ExecuteUpdateConnectionFlow())
                .putTemporaryData("name", "创建索引")
                .execute();
    }
}
